import {initState} from "./state";
import {compileToFunctions} from "./compiler/index";
import {mountComponent, callHook} from "./lifecycle.js";
import {mergeOptions} from "./utils";
import {nextTick} from "./observer/scheduler";


// vue的响应式原理
export function initMixin(Vue) {
  // 传入的构造函数的原型上添加方法：用户传入的配置项
  Vue.prototype._init = function (options) {
    // Vue的内部 $options 就是用户传递的所有参数
    const vm = this;
    // 这个options 就包含了用户创建实例时传入的所有属性 Vue.options
    vm.$options = mergeOptions(vm.constructor.options, options); // 用户传入的参数

    // 创建前,要开始处理数据和方法了
    callHook(vm, "beforeCreate"); // 调用生命周期函数 this.$options.beforeCreate()

    // initReject()
    // 挂载数据方法
    initState(vm); // 初始化状态 data/methods/props/computed/watcher
    // initProvide()

    callHook(vm, "created"); // 执行了created函数,为什么拿不到真实的dom
    // 需要通过模板进行渲染
    if (vm.$options.el) {
      // 用户传入了el属性
      vm.$mount(vm.$options.el);
    }
  };
  Vue.prototype.$mount = function (el) {
    // 可能是字符串 也可以传入一个dom对象
    const vm = this;
    el = vm.$el = document.querySelector(el); // 获取el属性

    // 如果同时传入 template 和render  默认会采用render 抛弃template，如果都没传就使用id="app"中的模板
    const opts = vm.$options;

    if (!opts.render) {
      let template = opts.template;
      if (!template && el) {
        // 应该使用外部的模板
        template = el.outerHTML;
      }

      // 模板转换成一个render函数，这个函数一执行就会返回当前组件的虚拟dom

      // function render() {
      //     with (this) {
      //         return _c('div', _S(msg.count))
      //     }
      // }
      const render = compileToFunctions(template); // 返回一个render函数，render函数执行会返回虚拟dom结构 // div v-if v-show {{  }}
      opts.render = render; // render函数执行返回一个当前组件的虚拟dom结构
    }

    // render函数已经构建完毕了，render函数的返回结果是虚拟dom
    // 走到这里说明不需要编译了 ，因为用户传入的就是 一个render函数
    mountComponent(vm, el); // 组件的挂载流程
  };
}
